<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>自定义富文本编辑器</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      line-height: 1.6;
      color: #333;
      padding: 20px;
      background-color: #f9f9f9;
    }

    .editor-container {
      margin: 0 auto;
      background: white;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      overflow: hidden;
    }

    .toolbar {
      display: flex;
      flex-wrap: wrap;
      padding: 8px;
      background: #f5f7fa;
      border-bottom: 1px solid #e1e4e8;
      gap: 4px;
    }

    .toolbar-group {
      display: flex;
      border-right: 1px solid #e1e4e8;
      padding-right: 8px;
      margin-right: 8px;
    }

    .toolbar-group:last-child {
      border-right: none;
      margin-right: 0;
      padding-right: 0;
    }

    .toolbar button {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 36px;
      height: 36px;
      background: white;
      border: 1px solid #d1d5da;
      border-radius: 4px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .toolbar button:hover {
      background: #f0f3f6;
      border-color: #c8d1d9;
    }

    .toolbar button.active {
      background: #e7ebee;
      border-color: #b9bec3;
    }

    .toolbar button svg {
      width: 18px;
      height: 18px;
      fill: #24292e;
    }

    .editor-content {
      min-height: 300px;
      padding: 16px;
      outline: none;
      line-height: 1.6;
    }

    .editor-content:focus {
      box-shadow: inset 0 0 0 1px #0366d6;
    }

    .editor-content img {
      max-width: 100%;
      height: auto;
      margin: 8px 0;
      border-radius: 4px;
    }

    .editor-content a {
      color: #0366d6;
      text-decoration: none;
    }

    .editor-content a:hover {
      text-decoration: underline;
    }

    .editor-content ul,
    .editor-content ol {
      padding-left: 2em;
      margin: 8px 0;
    }

    #imageUpload {
      display: none;
    }

    .status-bar {
      padding: 8px 16px;
      background: #f5f7fa;
      border-top: 1px solid #e1e4e8;
      font-size: 12px;
      color: #586069;
      display: flex;
      justify-content: space-between;
    }

    .upload-progress {
      display: none;
      width: 100%;
      height: 4px;
      background: #e1e4e8;
      margin-top: 8px;
      border-radius: 2px;
      overflow: hidden;
    }

    .upload-progress-bar {
      height: 100%;
      background: #28a745;
      width: 0%;
      transition: width 0.3s;
    }
  </style>
</head>
<body>
<div class="editor-container">
  <div class="toolbar">
    <div class="toolbar-group">
      <button data-command="bold" title="加粗 (Ctrl+B)">
        <svg viewBox="0 0 24 24">
          <path d="M15.6 11.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 7.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/>
        </svg>
      </button>
      <button data-command="italic" title="斜体 (Ctrl+I)">
        <svg viewBox="0 0 24 24">
          <path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"/>
        </svg>
      </button>
      <button data-command="underline" title="下划线 (Ctrl+U)">
        <svg viewBox="0 0 24 24">
          <path d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"/>
        </svg>
      </button>
    </div>

    <div class="toolbar-group">
      <button data-command="insertUnorderedList" title="无序列表">
        <svg viewBox="0 0 24 24">
          <path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/>
        </svg>
      </button>
      <button data-command="insertOrderedList" title="有序列表">
        <svg viewBox="0 0 24 24">
          <path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/>
        </svg>
      </button>
    </div>

    <div class="toolbar-group">
      <button data-command="justifyLeft" title="左对齐">
        <svg viewBox="0 0 24 24">
          <path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/>
        </svg>
      </button>
      <button data-command="justifyCenter" title="居中对齐">
        <svg viewBox="0 0 24 24">
          <path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/>
        </svg>
      </button>
      <button data-command="justifyRight" title="右对齐">
        <svg viewBox="0 0 24 24">
          <path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/>
        </svg>
      </button>
    </div>

    <div class="toolbar-group">
      <button data-command="createLink" title="插入链接 (Ctrl+K)">
        <svg viewBox="0 0 24 24">
          <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>
        </svg>
      </button>
      <button id="insertImage" title="插入图片">
        <svg viewBox="0 0 24 24">
          <path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14 6 17h12l-3.86-5.14z"/>
        </svg>
      </button>
      <input type="file" id="imageUpload" accept="image/*">
    </div>
  </div>

  <div id="editor" class="editor-content" contenteditable="true"></div>

  <div class="status-bar">
    <span id="charCount">0 字符</span>
    <div class="upload-progress" id="uploadProgress">
      <div class="upload-progress-bar" id="uploadProgressBar"></div>
    </div>
  </div>
</div>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const editor = document.getElementById('editor');
    const imageUpload = document.getElementById('imageUpload');
    const insertImageBtn = document.getElementById('insertImage');
    const charCount = document.getElementById('charCount');

    // 更新字符计数
    function updateCharCount() {
      const text = editor.innerText;
      charCount.textContent = `${text.length} 字符`;
    }

    // 初始化字符计数
    updateCharCount();
    editor.addEventListener('input', updateCharCount);

    // 工具栏按钮功能
    document.querySelectorAll('.toolbar button[data-command]').forEach(button => {
      button.addEventListener('click', function() {
        const command = this.getAttribute('data-command');
        // 处理特殊命令
        if (command === 'createLink') {
          const selection = window.getSelection();
          if (selection.toString().length === 0) {
            alert('请先选择要添加链接的文本');
            return;
          }
          const url = prompt('输入链接地址:', 'https://');
          if (url) {
            document.execCommand(command, false, url);
          }
        } else {
          document.execCommand(command, false, null);
          // 切换按钮激活状态
          if (['bold', 'italic', 'underline'].includes(command)) {
            const isActive = document.queryCommandState(command);
            this.classList.toggle('active', isActive);
          }
        }
        editor.focus();
      });
    });

    // 图片上传功能
    insertImageBtn.addEventListener('click', function() {
      imageUpload.click();
    });

    imageUpload.addEventListener('change', function(e) {
      const file = e.target.files[0];
      if (!file) return;

      // 验证文件类型
      if (!file.type.match('image.*')) {
        alert('请选择有效的图片文件 (JPEG, PNG, GIF等)');
        return;
      }

      // 验证文件大小 (限制为5MB)
      if (file.size > 5 * 1024 * 1024) {
        alert('图片大小不能超过5MB');
        return;
      }

      // 插入占位符
      let file_read = new FileReader()
      file_read.readAsDataURL(file)
      file_read.onload = e => {
        const placeholderId = e.target.result
        const placeholderHtml = `<img src="${placeholderId}" width="200px" />`;
        insertAtCursor(placeholderHtml);
      }

      // 重置文件输入，允许重复上传同一文件
      e.target.value = '';
    });

    // 在光标位置插入内容
    function insertAtCursor(html) {
      const selection = window.getSelection();

      if (selection.rangeCount > 0) {
        const range = selection.getRangeAt(0);
        range.deleteContents();

        const div = document.createElement('div');
        div.innerHTML = html;
        const frag = document.createDocumentFragment();

        while (div.firstChild) {
          frag.appendChild(div.firstChild);
        }

        range.insertNode(frag);

        // 移动光标到插入内容之后
        const newRange = document.createRange();
        newRange.setStartAfter(frag.lastChild || frag);
        newRange.collapse(true);
        selection.removeAllRanges();
        selection.addRange(newRange);
      } else {
        editor.innerHTML += html;
      }

      editor.focus();
    }

    // 添加键盘快捷键
    editor.addEventListener('keydown', function(e) {
      // Ctrl+B - 加粗
      if (e.ctrlKey && e.key === 'b') {
        e.preventDefault();
        document.execCommand('bold', false, null);
        document.querySelector('[data-command="bold"]').classList.toggle('active', document.queryCommandState('bold'));
      }

      // Ctrl+I - 斜体
      if (e.ctrlKey && e.key === 'i') {
        e.preventDefault();
        document.execCommand('italic', false, null);
        document.querySelector('[data-command="italic"]').classList.toggle('active', document.queryCommandState('italic'));
      }

      // Ctrl+U - 下划线
      if (e.ctrlKey && e.key === 'u') {
        e.preventDefault();
        document.execCommand('underline', false, null);
        document.querySelector('[data-command="underline"]').classList.toggle('active', document.queryCommandState('underline'));
      }

      // Ctrl+K - 插入链接
      if (e.ctrlKey && e.key === 'k') {
        e.preventDefault();
        const selection = window.getSelection();
        if (selection.toString().length > 0) {
          document.querySelector('[data-command="createLink"]').click();
        }
      }
    });

    // 初始化按钮状态
    function initButtonStates() {
      document.querySelector('[data-command="bold"]').classList.toggle('active', document.queryCommandState('bold'));
      document.querySelector('[data-command="italic"]').classList.toggle('active', document.queryCommandState('italic'));
      document.querySelector('[data-command="underline"]').classList.toggle('active', document.queryCommandState('underline'));
    }

    // 监听选择变化更新按钮状态
    document.addEventListener('selectionchange', function() {
      initButtonStates();
    });

    // 初始化按钮状态
    initButtonStates();
  });
</script>
</body>
</html>
